package com.li.spring.aop.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LoggerAspect {

    /**
     * 前置通知：使用@Before注解标识，在被代理的目标方法前执行
     * 返回通知：使用@AfterReturning注解标识，在被代理的目标方法成功结束后执行（寿终正寝）
     * 异常通知：使用@AfterThrowing注解标识，在被代理的目标方法异常结束后执行（死于非命）
     * 后置通知：使用@After注解标识，在被代理的目标方法最终结束后执行（盖棺定论）
     * 环绕通知：使用@Around注解标识，使用try...catch...finally结构围绕整个被代理的目标方法，包括上面四种通知对应的所有位置
     */

    @Pointcut("execution(* com.li.spring.aop.annotation.*.*(..))")
    public void pointCut(){}

    /*
    * @Before在返回值之前执行
    * */
//    @Before("execution(public int com.li.spring.aop.annotation.CalculatorImpl.add(int, int))")
//    @Before("execution(* com.li.spring.aop.annotation.*.*(..))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect, 方法: " + signature.getName() + "，参数: " + Arrays.toString(args));
    }

    /*
    * @After在finally之后实现
    * */
    @After("pointCut()")
    public void afterAdviceMethod(){
        System.out.println("LoggerAspect, 后置通知");
    }

    /*
     * @AfterReturning在返回值返回之后实现，如果执行时异常不会实现
     * 若需获取return的值,则需在注解中加入returning属性，来获取参数
     * */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result){
        System.out.println("LoggerAspect, 返回通知");
    }

    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Throwable e){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect, 方法:" + signature.getName() + "，异常:" + e);
    }

    @Around("pointCut()")
    public Object aroundAdviceMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object proceed = null;
        try {
            System.out.println("环绕通知，前置通知的位置");
            //表示目标方法的执行
            proceed = proceedingJoinPoint.proceed();
            System.out.println("环绕通知， 返回通知的位置");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知， 异常通知的位置");
        } finally {
            System.out.println("环绕通知， 后置通知的位置");
        }
        return proceed;
    }
}
